Skip to content

feat!: spec sync 3.21.0 → 3.22.0 (v5.0.0) — drop V1 order API, add RFQ quote endpoints#459

Merged
TexasCoding merged 1 commit into
mainfrom
fix/spec-drift-2026-06-26
Jun 26, 2026
Merged

feat!: spec sync 3.21.0 → 3.22.0 (v5.0.0) — drop V1 order API, add RFQ quote endpoints#459
TexasCoding merged 1 commit into
mainfrom
fix/spec-drift-2026-06-26

Conversation

@TexasCoding

Copy link
Copy Markdown
Owner

Summary

Syncs the upstream OpenAPI spec 3.21.0 → 3.22.0 and ships SDK v5.0.0.
Resolves both open spec-drift issues (the nightly strict-contract failure and the
drift report).

The headline change is breaking: Kalshi removed the V1 order-write
endpoints
from the spec, so the SDK removes the V1 order methods and their
dedicated models. Order writes now go exclusively through the V2
/portfolio/events/orders family (the *_v2 methods, which have existed since
3.18.0). The remaining drift is additive and reconciled in place, plus the three
new RFQ-scoped quote endpoints are implemented.

This was triaged with the maintainer, who chose "match the spec exactly
(breaking)"
over a deprecation shim, and "add the new RFQ endpoints now."

Removed (breaking)

  • V1 order-write methods on client.orders (sync + async): create,
    cancel, batch_create, batch_cancel, amend, decrease. The underlying
    endpoints (POST/DELETE /portfolio/orders, /portfolio/orders/batched,
    /portfolio/orders/{id}/amend|decrease) were removed from the spec in 3.22.0.
  • V1 order models (no longer exported): CreateOrderRequest,
    AmendOrderRequest, AmendOrderResponse, DecreaseOrderRequest,
    BatchCreate*/BatchCancel* request/response models,
    BatchCancelOrdersRequestOrder, and the ActionLiteral alias.
  • Dead quote filters event_ticker / market_ticker on
    GET /communications/quotes (removed upstream).

Added

  • RFQ-scoped quote actions on client.communications.quotes (sync + async):
    accept_for_rfq, confirm_for_rfq, delete_for_rfq — backing
    PUT/DELETE /communications/rfqs/{rfq_id}/quotes/{quote_id}[/accept|/confirm].
  • cancel_v2 market_ticker query param + BatchCancelOrdersV2RequestOrder.market_ticker
    (required when exchange_index == -1; the model now accepts -1 to auto-route).
  • SubaccountBalance.exchange_index.
  • Perps SCM: MarketSettlementEstimate, SettlementEstimate.positions,
    GetSettlementEstimateResponse.prev_settlement_prices.

Migration

V1 → V2 mapping is documented in docs/migration.md (new v4 → v5.0.0 section)
and CHANGELOG.md. Reads (get/list/queue_positions/fills) are unchanged.

Verification

  • uv run pytest tests/4097 passed, 311 skipped, 0 failed
  • uv run mypy kalshi/ → clean (strict, 157 files)
  • uv run ruff check . → clean
  • Contract drift suites (TestRequestParamDrift / TestRequestBodyDrift /
    TestSpecDrift / TestPerpsScmSpecDrift) green against the re-vendored spec.
  • New behavioral tests cover all 6 new RFQ-scoped methods (sync + async + auth
    guards) and the exchange_index=-1 auto-route path.

Docs were swept for the V2-only order API; an adversarial multi-agent review pass
caught and fixed several example bugs (missing required self_trade_prevention_type,
an invalid STP value, stale operation counts).

Closes #454
Closes #458


🤖 Generated with Claude Code

…Q quote endpoints

Upstream OpenAPI 3.22.0 removed the V1 order-write endpoints; the SDK drops the
V1 order methods and their dedicated models in favor of the V2
`/portfolio/events/orders` family (available since 3.18.0). Also reconciles the
remaining additive drift and adds the three new RFQ-scoped quote endpoints.

Removed (breaking):
- orders.{create,cancel,batch_create,batch_cancel,amend,decrease} (sync + async)
- V1 order models: CreateOrderRequest, AmendOrderRequest, AmendOrderResponse,
  DecreaseOrderRequest, BatchCreate*/BatchCancel* request/response models,
  BatchCancelOrdersRequestOrder, and the ActionLiteral alias
- event_ticker/market_ticker filters on GET /communications/quotes

Added:
- communications.quotes.{accept_for_rfq,confirm_for_rfq,delete_for_rfq}
- cancel_v2 market_ticker query param; BatchCancelOrdersV2RequestOrder.market_ticker
- SubaccountBalance.exchange_index
- perps SCM: MarketSettlementEstimate, SettlementEstimate.positions,
  GetSettlementEstimateResponse.prev_settlement_prices

Contract drift suite, mypy (strict), and ruff all pass; 4086 tests green.

Closes #454
Closes #458

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@TexasCoding TexasCoding added spec-drift Upstream OpenAPI/AsyncAPI spec changed since last sync breaking Backwards-incompatible change labels Jun 26, 2026
@claude

claude Bot commented Jun 26, 2026

Copy link
Copy Markdown

Code Review — feat!: spec sync 3.21.0 → 3.22.0 (v5.0.0)

Overview

This is a well-executed breaking-change release. The core operation — removing the V1 order-write surface now that Kalshi dropped it from the spec — is done surgically: every V1 method, model, import, test, contract entry, and EXCLUSION is cleaned up without touching adjacent code. The three new RFQ-scoped quote endpoints follow existing conventions exactly. Migration guide and CHANGELOG are comprehensive. Test suite passes (4097/0).


Issues / Concerns

1. cancel_v2 with market_ticker has no unit test (low severity)

The BatchCancelOrdersV2RequestOrder.exchange_index=-1, market_ticker=... path is tested (test_auto_route_exchange_index_minus_one), but the singular cancel_v2(order_id, exchange_index=-1, market_ticker="MKT-A") path has no corresponding test. Given that this is a spec-required pairing, worth a quick test:

@respx.mock
def test_cancel_v2_auto_route_sends_market_ticker(self, orders: OrdersResource) -> None:
    route = respx.delete(
        "https://test.kalshi.com/trade-api/v2/portfolio/events/orders/ord-1"
    ).mock(return_value=httpx.Response(200, json={...}))
    orders.cancel_v2("ord-1", exchange_index=-1, market_ticker="MKT-A")
    assert route.calls[0].request.url.params["market_ticker"] == "MKT-A"

2. exchange_index=-1 + missing market_ticker goes unvalidated (low severity)

Both cancel_v2 and BatchCancelOrdersV2RequestOrder document "required when exchange_index is -1" but do not enforce it. A caller who passes exchange_index=-1 without market_ticker gets a server-side 400 rather than a fail-fast SDK error. This is consistent with how the rest of the SDK handles conditional spec requirements, so it is acceptable — but a model_validator on BatchCancelOrdersV2RequestOrder (parallel to DecreaseOrderV2Request._enforce_reduce_xor) would catch it earlier and give a clearer error message.

3. SubaccountBalance.exchange_index declared as required int — deserialization risk (medium severity)

subaccount_number: int
exchange_index: int   # required, no default
balance: DollarDecimal

Declaring this without a default means any API response that omits exchange_index raises ValidationError. The spec says it is required in 3.22.0, but if production is still rolling out, or a user is on an older environment, they will get a runtime crash the moment they call any endpoint that returns subaccount balances. Consider exchange_index: int | None = None until the field is confirmed in all environments. Since the model has extra = "allow", making it optional costs nothing and avoids a hard failure window.


Positives worth calling out

  • RFQ method designaccept_for_rfq correctly mirrors the @overload pattern from QuotesResource.accept, and shares _build_accept_quote_body. No logic duplication.
  • Path parameter sanitization_seg(rfq_id, name='rfq_id') and _seg(quote_id, name='quote_id') on every new RFQ URL.
  • confirm_for_rfq comment — the # json={} forces Content-Type note on the empty-body PUT matches the existing confirm method pattern and is the right call.
  • ge=0 removal on BatchCancelOrdersV2RequestOrder.exchange_index — the comment explaining the -1 sentinel is clear.
  • Contract drift test housekeeping — V1 entries fully removed from METHOD_ENDPOINT_MAP, BODY_MODEL_MAP, EXCLUSIONS, and _contract_map.py. No stale entries left behind.
  • Migration doc — the V1 to V2 mapping table is exactly what users need.

Verdict

Approve with one suggested fix: the SubaccountBalance.exchange_index required-vs-optional question (point 3) is the only change that could break users silently at runtime. Everything else is clean.

@TexasCoding TexasCoding merged commit a1c6bd0 into main Jun 26, 2026
6 checks passed
@TexasCoding TexasCoding deleted the fix/spec-drift-2026-06-26 branch June 26, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Backwards-incompatible change spec-drift Upstream OpenAPI/AsyncAPI spec changed since last sync

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spec drift 2026-06-22: openapi 3.21.0 → 3.21.0 Nightly spec-drift: strict contract tests failing (since 2026-06-20)

1 participant